package com.cpr.demo.thread.itl2;

import org.assertj.core.util.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author Chen Panrui
 * created at 2021/9/8 15:07
 * Q: 为什么会有这个类？
 * A: 了解 InheritableThreadLocal. (简约版的 TreadPoolContextHolder.java 参考： {@link com.cpr.demo.thread.TransmittableThreadLocalTest})
 * 问题B：如果我们要实现这个功能，这么做？在线程池中work线程执行时获取的上下文为 执行 该work线程的那个线程的上下文 ，而不是 创建 worker线程的 那个线程的上下文。
 * 问题B的解决参考：{@link com.cpr.demo.thread.TransmittableThreadLocalTest}
 */
public class TreadPoolContextHolder {

    private static final Logger LOGGER = LoggerFactory.getLogger(TreadPoolContextHolder.class);

    // 定义线程池
    ExecutorService executor = new ThreadPoolExecutor(1, 1, 300, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>());

    private void initContext(String param) {
        // 1.主线程
        Context context = new Context();
        context.setProductNo(param);
        ContextHolder.setContext(context);
    }

    private void testExecute() {
        try {
            List<Future<Integer>> futureList = Lists.newArrayList();
            futureList.add(executor.submit(new TaskCall(0)));
        } catch (Exception e) {
            LOGGER.error("error:", e);
        }
    }


    private class TaskCall implements Callable<Integer> {
        int p;

        TaskCall(int p) {
            this.p = p;
        }

        public Integer call() throws Exception {
            try {
                LOGGER.info("task{},productNo={}", p, ContextHolder.getContext().getProductNo());
            } catch (Exception e) {

            }
            return 0;
        }
    }


    public static void main(String[] args) {
        // 线程池中的worker线程 的父线程=main线程
        final TreadPoolContextHolder test = new TreadPoolContextHolder();
        // main-线程 当前线程 里面的InheritableThreadLocal<Context>放东西
        /*
         * 严谨点，
         * Q: 我们先初始化了线程池，再在主线程设置了InheritableThreadLocal，线程池中的worker线程的inheritableThreadLocals对象也会有值？？？
         * A&Q: 不会。那为什么这儿能打印？
         * A: 当executor执行的时候，若当前worker数量 小于 core配置数量才会new一个worker，此时new出来的worker线程，才会同步父线程的inheritableThreadLocals.
         * 测试例子：InheritableThreadLocalTest2.java
         */
        test.initContext("t0000");
        // 使用线程池中的worker线程 打印 InheritableThreadLocal<Context>里面的东西，这儿肯定就是 t0000
        // 凡用executor打印的 都是 t0000
        test.testExecute();

        try {
            Thread.sleep(1000);
        } catch (Exception e) {

        }

        LOGGER.info("---------thread1--------");
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                // thread1线程 在 当前线程里面的InheritableThreadLocal<Context>放东西
                test.initContext("t0001");
                // 凡用executor打印的 都是 t0000
                test.testExecute();
                // 从当前线程的Thread.inheritableThreadLocals 里面获取(K=InheritableThreadLocal<Context[t0001]>的实例对应的V) 肯定就是t0001
                LOGGER.info("thread 1 context,productNo=" +
                        ContextHolder.getContext().getProductNo());
            }
        });
        // 启动新线程
        thread1.start();

        try {
            Thread.sleep(1000);
        } catch (Exception e) {

        }

        LOGGER.info("---------thread2--------");
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                test.initContext("t0002");
                test.testExecute();
                LOGGER.info("thread 2 context,productNo=" +
                        ContextHolder.getContext().getProductNo());
            }
        });
        // 启动新线程
        thread2.start();


        try {
            Thread.sleep(1000);
        } catch (Exception e) {

        }

        LOGGER.info("--------- main --------");
        // 2.来主线程获取
        LOGGER.info("main context,productNo=" +
                ContextHolder.getContext().getProductNo());
    }

}
